#include "EspInstanceWindow.h"

EspInstanceWindow::EspInstanceWindow()
{
    // m_window_pixel_format = s_window_pixel_format_rgb565;
}

void EspInstanceWindow::OnInit()
{
    m_lcd_panel_handle = bsp_lcd_init();
    esp_lcd_rgb_panel_get_frame_buffer(m_lcd_panel_handle, 2, (void **)&m_frame_buffers_0, (void **)&m_frame_buffers_1);
    m_frame_buffers = m_frame_buffers_0;

    touchpad_init();

    SetScreenWidth(LCD_WIDTH);
    SetScreenHeight(LCD_HEIGHT);
    OnWindowSize(LCD_WIDTH, LCD_HEIGHT);

    MainWindow::OnInit();
}

void EspInstanceWindow::OnEvent()
{
    // 事件回调帧率不能调高, 否则获取的状态可能是获取信息后的重置态
    // 程序获取按下信息后会重置抬起, 可能导致下一帧获取是重置数据, 降低获取评率避免
    static int s_last_mouse_x = 0;
    static int s_last_mouse_y = 0;
    static bool s_mouse_pressed = false;

    if (touchpad_is_pressed())
    {
        touchpad_get_xy(&s_last_mouse_x, &s_last_mouse_y);
        if (s_mouse_pressed)
        {
            OnMouseMove(s_last_mouse_x, s_last_mouse_y, IEvent::MOUSE_BUTTON_LEFT);
        }
        else
        {
            // std::cout << "OnMouseDown x = " << s_last_mouse_x << " y = " << s_last_mouse_y << std::endl;
            OnMouseDown(s_last_mouse_x, s_last_mouse_y, IEvent::MOUSE_BUTTON_LEFT);
        }
        s_mouse_pressed = true;
    }
    else
    {
        if (s_mouse_pressed)
        {
            // std::cout << "OnMouseUp x = " << s_last_mouse_x << " y = " << s_last_mouse_y << std::endl;
            OnMouseUp(s_last_mouse_x, s_last_mouse_y, IEvent::MOUSE_BUTTON_LEFT);
        }
        s_mouse_pressed = false;
    }
    MainWindow::OnEvent();
}

void EspInstanceWindow::Render()
{
    auto begin_time = PlatformApplication::GetInstance()->GetTimeStamp();
    MainWindow::Render();
    auto end_time = PlatformApplication::GetInstance()->GetTimeStamp();
    if (end_time - begin_time > 40)
    {
        std::cout << "render use time: " << (end_time - begin_time) << "ms" << std::endl;
    }
}

void EspInstanceWindow::Flush(int x, int y, int width, int height)
{
    auto begin_time = PlatformApplication::GetInstance()->GetTimeStamp();
    auto painter = GetPainter();
    auto painter_stride = painter->GetStride();
    auto painter_buffer = painter->GetPixels();
    auto window_width = GetWindowWidth();
    auto window_height = GetWindowHeight();
    x = x < 0 ? 0 : x;
    y = y < 0 ? 0 : y;
    width = (x + width) > window_width ? (window_width - x) : width;
    height = (y + height) > window_height ? (window_height - y) : height;

    if ((width * height * 4) > (window_width * window_height))
    {
        m_frame_buffers = m_frame_buffers == m_frame_buffers_0 ? m_frame_buffers_1 : m_frame_buffers_0;
        x = 0;
        y = 0;
        width = window_width;
        height = window_height;
    }

    if (width > 0 && height > 0)
    {
        auto dst_pixels = reinterpret_cast<uint16_t *>(m_frame_buffers);
        auto dst_stride = window_width;
        auto src_pixels = reinterpret_cast<uint32_t *>(painter_buffer);
        auto src_stride = painter_stride;
        auto dst_start_line_pixel = dst_pixels + y * dst_stride + x;
        auto dst_end_line_pixel = dst_start_line_pixel + height * dst_stride;
        auto src_start_line_pixel = src_pixels + y * src_stride + x;
        while (dst_start_line_pixel < dst_end_line_pixel)
        {
            auto dst_start_pixel = dst_start_line_pixel;
            auto dst_end_pixel = dst_start_line_pixel + width;
            auto src_start_pixel = src_start_line_pixel;
            while (dst_start_pixel < dst_end_pixel)
            {
                *dst_start_pixel = ((((*src_start_pixel) & 0xF80000) >> 8) | (((*src_start_pixel) & 0xFC00) >> 5) | (((*src_start_pixel) & 0xF8) >> 3));
                dst_start_pixel++;
                src_start_pixel++;
            }
            dst_start_line_pixel += dst_stride;
            src_start_line_pixel += src_stride;
        }
        bsp_lcd_flush(m_lcd_panel_handle, x, y, width, height, m_frame_buffers);
    }
    auto end_time = PlatformApplication::GetInstance()->GetTimeStamp();
    std::cout << "Flush time: " << (end_time - begin_time) << "ms x = " << x << " y = " << y << " width = " << width << " height = " << height << std::endl;
}

void EspInstanceWindow::DumpMemory()
{
    ESP_LOGI("MEMORY", "Heap free:       %d bytes", esp_get_free_heap_size());
    ESP_LOGI("MEMORY", "Internal free:   %d bytes", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
    ESP_LOGI("MEMORY", "DMA free:        %d bytes", heap_caps_get_free_size(MALLOC_CAP_DMA));
    ESP_LOGI("MEMORY", "SPI SRAM free:   %d bytes", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));

    UBaseType_t stack_mark = uxTaskGetStackHighWaterMark(NULL);
    ESP_LOGI("MEMORY", "Current task stack remaining: %d words (%d bytes)", stack_mark, stack_mark * sizeof(StackType_t));
}
